package cn.wsh.basic.service.impl;

import cn.wsh.basic.constant.BaseConstant;
import cn.wsh.basic.dto.LoginDto;
import cn.wsh.basic.exception.BusinessException;
import cn.wsh.basic.jwt.JwtUtils;
import cn.wsh.basic.jwt.LoginData;
import cn.wsh.basic.jwt.RsaUtils;
import cn.wsh.basic.service.ILoginService;
import cn.wsh.basic.util.*;
import cn.wsh.org.domain.Employee;
import cn.wsh.org.mapper.EmployeeMapper;
import cn.wsh.system.domain.Menu;
import cn.wsh.user.domain.Logininfo;
import cn.wsh.user.domain.User;
import cn.wsh.user.domain.Wxuser;
import cn.wsh.user.mapper.LogininfoMapper;
import cn.wsh.user.mapper.UserMapper;
import cn.wsh.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private EmployeeMapper employeeMapper;
    @Value("${jwt.rsa.pri}")//SEL
    private String priKey;

    @Override
    public Map<String, Object> account(LoginDto dto) {
        //1.参数校验
        if(StringUtils.isEmpty(dto.getUsername())
                || StringUtils.isEmpty(dto.getType())
                || StringUtils.isEmpty(dto.getPassword())
        ){
            throw new BusinessException("参数异常！！！");
        }
        //2.根据username + type  查询t_logininfo  同时比较username phone email
        Logininfo logininfoTmp = logininfoMapper.loadByDto(dto);
        if(logininfoTmp == null){
            throw new BusinessException("账号不存在！！");
        }
        //3.判断密码是否一致
        String salt = logininfoTmp.getSalt();
        String md5Pwd = MD5Utils.encrypByMd5(dto.getPassword() + salt);
        if(!md5Pwd.equals(logininfoTmp.getPassword())){
            throw new BusinessException("密码不正确！！！");
        }
        //4.是否被禁用
        if(logininfoTmp.getDisable().intValue() == 0){
            throw new BusinessException("账号被禁用，请联系管理员");
        }
        //5.将登录信息存储到redis
        /*String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(
                token,
                logininfoTmp,   // 要把对象存储到redis 对象需要实现序列化接口
                30,
                TimeUnit.MINUTES
        );
        //6.返回token + logininfo
        Map<String, Object> map = new HashMap<>();
        map.put("token",token);
        //作为高级技师，应该考虑到将一些敏感字段置空  salt pwd
        logininfoTmp.setSalt(null);
        logininfoTmp.setPassword(null);
        map.put("logininfo", logininfoTmp);*/
        //调整登录成功之后的处理逻辑，使用jwt来代替redis存储登录信息
        return loginSuccessJwtHandler(logininfoTmp);
    }

    public Map<String, Object> loginSuccessJwtHandler(Logininfo logininfo){
        try {
            LoginData loginData = new LoginData();
            if(logininfo.getType().intValue() == 1){//查询用户信息
                User user = userMapper.loadUserByLogininfoId(logininfo.getId());
                user.setSalt(null);
                user.setPassword(null);
                loginData.setUser(user);
            }else{//查询员工信息 + 对应的权限信息 + 对应的菜单信息
                Employee employee = employeeMapper.loadEmpByLogininfoId(logininfo.getId());
                employee.setSalt(null);
                employee.setPassword(null);
                loginData.setAdmin(employee);
                List<String> snList = employeeMapper.loadPerssionSnByLogininfoId(logininfo.getId());
                List<Menu> menus = employeeMapper.loadMenusByLogininfoId(logininfo.getId());
                loginData.setPermissions(snList);//当前登录人所拥有的的按钮权限
                loginData.setMenus(menus);//当前登录人所拥有的菜单
            }

            //获取私钥
            PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource(priKey).getFile());
            //使用工具类进行加密，得到jwt
            String jwtStr = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 12*60);
            log.info("您的jwt是：{}",jwtStr);
            Map<String, Object> map = new HashMap<>();
            map.put("token",jwtStr);
            //跟权限挂钩的也要返回
            if(loginData.getUser() != null){
                map.put("user",loginData.getUser());
            }else{
                map.put("user",loginData.getAdmin());
            }
            map.put("menus",loginData.getMenus());//前端需要做动态菜单显示
            map.put("permissions",loginData.getPermissions());//前端需要做动态按钮显示
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    @Override
    public AjaxResult wechat(Map<String, String> param) {
        String code = param.get("code");
//        2.根据code从微信去获取token + openId
        String tokenUrl = BaseConstant.LoginConstant.TOKEN_URL
                .replace("APPID", BaseConstant.LoginConstant.APPID)
                .replace("SECRET", BaseConstant.LoginConstant.SECRET)
                .replace("CODE", code);
        String tokenStr = HttpClientUtils.httpGet(tokenUrl);
        JSONObject jsonObject = JSONObject.parseObject(tokenStr);
        String accessToken = jsonObject.getString("access_token");
        String openId = jsonObject.getString("openid");
//        3.根据openId查询本地的t_wxuser，看是否曾今已经扫码过
        Wxuser wxuser = wxuserMapper.loadByOpenId(openId);
        if(wxuser != null && wxuser.getUserId() != null){
//        3.1. 有值且t_wxuser表中的user_id有值 曾今已经扫过，本次是第二...N扫码
//                直接免密登录
            Logininfo logininfo = logininfoMapper.loadbyUserId(wxuser.getUserId());

            return AjaxResult.me().setSuccess(true).setResultObj(loginSuccessJwtHandler(logininfo));
        }
//        3.2. 没有，需要走微信绑定流程
//        需要返回：token + openId
        String binderParams = "?accessToken="+accessToken+"&openId="+openId;
        return AjaxResult.me().setSuccess(false).setResultObj(binderParams);
    }

    @Override
    public Map<String, Object> binder(Map<String, String> param) {
        String phone = param.get("phone");
        String verifyCode = param.get("verifyCode");
        String accessToken = param.get("accessToken");
        String openId = param.get("openId");
//        7.后端接收到参数,各种校验
//        8.验证码是否正确
        String smsCodeKey = BaseConstant.SmsConstant.BUSINESS_BINDER_PREFIX+phone;//binder:15982485856
        Object smsCodeTmp = redisTemplate.opsForValue().get(smsCodeKey);
        if(smsCodeTmp == null){
            throw new BusinessException("验证码已失效，请重新获取！！");
        }
        String codeTmp = smsCodeTmp.toString().split(":")[0]; //使用历史的验证码
        if(!verifyCode.equalsIgnoreCase(codeTmp)){
            throw new BusinessException("请填写正确的验证码！！");
        }
//        9.手机号是否曾经被注册
        User user = userMapper.loadByPhone(phone);
        Logininfo logininfo = null;
        if(user != null){
            //        9.1 已经被注册了
//                查出来
            logininfo = logininfoMapper.loadbyUserId(user.getId());
        }else{
//        9.2 没有被注册过
//        创建新的t_user、t_logininfo
            user = initUser(phone);
            logininfo = user2Logininfo(user);
            logininfoMapper.save(logininfo);//返回自增ID
            user.setLogininfoId(logininfo.getId());
            userMapper.save(user);
        }
//        10.根据token+openId查询微信用户信息
        String wxUserUrl = BaseConstant.LoginConstant.WXUSER_URL
                .replace("ACCESS_TOKEN",accessToken)
                .replace("OPENID",openId);
        String wxUserStr = HttpClientUtils.httpGet(wxUserUrl);
//        10.1 根据微信返回的信息创建t_wxuser，设置初始值
        Wxuser wxuser = JSONObject.parseObject(wxUserStr, Wxuser.class);
//        10.2 t_wxuser绑定平台user_id
//        10.3 保存数据
        wxuser.setUserId(user.getId());
        wxuserMapper.save(wxuser);
//        11.免密登录

        return loginSuccessJwtHandler(logininfo);
    }

    private Logininfo user2Logininfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user, logininfo);
        //一定不要忘记设置type
        logininfo.setType(1);
        return logininfo;
    }

    /**
     * 初始化user对象
     * @param phone
     * @return
     */
    private User initUser(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);
        //盐值
        String salt = StrUtils.getComplexRandomString(32);
        // 默认密码是1  发短信告诉他自己去修改
        String md5Pwd = MD5Utils.encrypByMd5("1" + salt);
        user.setSalt(salt);
        user.setPassword(md5Pwd);
        return user;
    }


}
